//#########################################################################
//                              WackiEngie
//           Copyright (c) 2014 Marc Wacker. All rights reserved.
//#########################################################################

#pragma once
#ifndef TESTSTSTS
#define TESTSTSTS

#include "WeUtilPrerequisites.h"

namespace WackiEngine
{

	template <typename T>
	using BasicString = std::basic_string<T>;

	// narrow string used for handling ASCII text
	typedef BasicString<char> String;
	
	// wide string for handling unicode text
	typedef BasicString<wchar_t> WString;
	
	// narow string sream
	typedef std::basic_stringstream<char> StringStream;

	// wide string stream
	typedef std::basic_stringstream<wchar_t> WStringStream;
	
	class StringUtil
	{
	public:
		// removes any whitespace characters from beginning and/or end of the string
		static void trim(String& str, bool left = true, bool right = true);

		static void trim(WString& str, bool left = true, bool right = true);

		// removes specific characters from beginning and/or end of the string
		static void trim(String& str, const String& delims, bool left = true, bool right = true);

		static void trim(WString& str, const WString& delims, bool left = true, bool right = true);

		// returns a vector of strings containing all of the substrings delimited by the provided delimiter characters
		static Vector<String> split(const String& str, const String& delims = "\t\n ", uint32 maxSplits = 0);

		static Vector<WString> split(const WString& str, const WString& delims = L"\t\n ", uint32 maxSplits = 0);

		// returns a vector of strings containing all the substrings delimited by the provided delimiter characters
		// or the double delimiters used for including normal delimit rcharacters in the tokenized string
		static Vector<String> tokenize(const String& str, const String& delims = "\t\n ", const String& doubleDelims = "\"", uint32 maxSplits = 0);

		static Vector<WString> tokenize(const WString& str, const WString& delims = L"\t\n ", const WString& doubleDelims = L"\"", uint32 maxSplits = 0);

		static void toLowerCase(String& str);

		static void toLowerCase(WString& str);

		static void toUpperCase(String& str);
		
		static void toUpperCase(WString& str);

		// retrns whether the string begins with the pattern passed in
		static bool startsWith(const String& str, const String& pattern, bool caseSensitive = false);

		static bool startsWith(const WString& str, const WString& pattern, bool caseSensitive = false);

		// returns wether the string ends with the pattern passed in
		static bool endsWith(const String& str, const String& pattern, bool caseSensitive = false);

		static bool endsWith(const WString& str, const WString& pattern, bool caseSensitive = false);

		// returns true if the string matches the provide pattern. pattern may use a "*" wildcard for matching any characters
		static bool match(const String& str, const String& pattern, bool caseSensitive = true);

		static bool match(const WString& str, const WString& pattern, bool caseSensitive = true);

		// replace all instances of a substring with an other substring
		static const String replaceAll(const String& source, const String& replaceWhat, const String& replaceWith);

		static const WString replaceAll(const WString& source, const WString& replaceWhat, const WString& replaceWith);

		// constant blank strings
		static const String BLANK;
		static const WString WBLANK;

	private:

		template <class T>
		static typename Vector<typename BasicString<T>> splitInternal(const typename BasicString<T>& str, const typename BasicString<T>& delims, uint32 maxSplits)
		{
			Vector<BasicString<T>> ret;
			// Pre-allocate some space for performance
			ret.reserve(maxSplits ? maxSplits + 1 : 10);    // 10 is guessed capacity for most case

			unsigned int numSplits = 0;

			// Use STL methods 
			size_t start, pos;
			start = 0;
			do
			{
				pos = str.find_first_of(delims, start);
				if(pos == start)
				{
					// Do nothing
					start = pos + 1;
				}
				else if(pos == BasicString<T>::npos || (maxSplits && numSplits == maxSplits))
				{
					// Copy the rest of the string
					ret.push_back(str.substr(start));
					break;
				}
				else
				{
					// Copy up to delimiter
					ret.push_back(str.substr(start, pos - start));
					start = pos + 1;
				}
				// parse up to next real data
				start = str.find_first_not_of(delims, start);
				++numSplits;

			} while(pos != BasicString<T>::npos);

			return ret;
		}

		template <class T>
		static typename Vector<typename BasicString<T>> tokenizeInternal(const typename BasicString<T>& str, const typename BasicString<T>& singleDelims,
			const typename BasicString<T>& doubleDelims, uint32 maxSplits)
		{
			Vector<BasicString<T>> ret;
			// Pre-allocate some space for performance
			ret.reserve(maxSplits ? maxSplits + 1 : 10);    // 10 is guessed capacity for most case

			unsigned int numSplits = 0;
			BasicString<T> delims = singleDelims + doubleDelims;

			// Use STL methods 
			size_t start, pos;
			T curDoubleDelim = 0;
			start = 0;
			do
			{
				if(curDoubleDelim != 0)
				{
					pos = str.find(curDoubleDelim, start);
				}
				else
				{
					pos = str.find_first_of(delims, start);
				}

				if(pos == start)
				{
					T curDelim = str.at(pos);
					if(doubleDelims.find_first_of(curDelim) != BasicString<T>::npos)
					{
						curDoubleDelim = curDelim;
					}
					// Do nothing
					start = pos + 1;
				}
				else if(pos == BasicString<T>::npos || (maxSplits && numSplits == maxSplits))
				{
					if(curDoubleDelim != 0)
					{
						//Missing closer. Warn or throw exception?
					}
					// Copy the rest of the string
					ret.push_back(str.substr(start));
					break;
				}
				else
				{
					if(curDoubleDelim != 0)
					{
						curDoubleDelim = 0;
					}

					// Copy up to delimiter
					ret.push_back(str.substr(start, pos - start));
					start = pos + 1;
				}
				if(curDoubleDelim == 0)
				{
					// parse up to next real data
					start = str.find_first_not_of(singleDelims, start);
				}

				++numSplits;

			} while(pos != BasicString<T>::npos);

			return ret;
		}

		template <class T>
		static bool startsWithInternal(const typename BasicString<T>& str, const typename BasicString<T>& pattern, bool caseSensitive)
		{
			size_t thisLen = str.length();
			size_t patternLen = pattern.length();
			if(thisLen < patternLen || patternLen == 0)
				return false;

			BasicString<T> startOfThis = str.substr(0, patternLen);
			BasicString<T> tmpPattern = pattern;
			if(!caseSensitive) {
				StringUtil::toLowerCase(startOfThis);
				StringUtil::toLowerCase(tmpPattern);
			}

			return (startOfThis == tmpPattern);
		}

		template <class T>
		static bool endsWithInternal(const typename BasicString<T>& str, const typename BasicString<T>& pattern, bool caseSensitive)
		{
			size_t thisLen = str.length();
			size_t patternLen = pattern.length();
			if(thisLen < patternLen || patternLen == 0)
				return false;

			BasicString<T> endOfThis = str.substr(thisLen - patternLen, patternLen);
			BasicString<T> tmpPattern = pattern;
			if(!caseSensitive) {
				StringUtil::toLowerCase(endOfThis);
				StringUtil::toLowerCase(tmpPattern);
			}

			return (endOfThis == tmpPattern);
		}

		template <class T>
		static bool matchInternal(const typename BasicString<T>& str, const typename BasicString<T>& pattern, bool caseSensitive)
		{
			BasicString<T> tmpStr = str;
			BasicString<T> tmpPattern = pattern;
			if(!caseSensitive)
			{
				StringUtil::toLowerCase(tmpStr);
				StringUtil::toLowerCase(tmpPattern);
			}

			BasicString<T>::const_iterator strIt = tmpStr.begin();
			BasicString<T>::const_iterator patIt = tmpPattern.begin();
			BasicString<T>::const_iterator lastWildCardIt = tmpPattern.end();
			while(strIt != tmpStr.end() && patIt != tmpPattern.end())
			{
				if(*patIt == '*')
				{
					lastWildCardIt = patIt;
					// Skip over looking for next character
					++patIt;
					if(patIt == tmpPattern.end())
					{
						// Skip right to the end since * matches the entire rest of the string
						strIt = tmpStr.end();
					}
					else
					{
						// scan until we find next pattern character
						while(strIt != tmpStr.end() && *strIt != *patIt)
							++strIt;
					}
				}
				else
				{
					if(*patIt != *strIt)
					{
						if(lastWildCardIt != tmpPattern.end())
						{
							// The last wildcard can match this incorrect sequence
							// rewind pattern to wildcard and keep searching
							patIt = lastWildCardIt;
							lastWildCardIt = tmpPattern.end();
						}
						else
						{
							// no wildwards left
							return false;
						}
					}
					else
					{
						++patIt;
						++strIt;
					}
				}

			}

			// If we reached the end of both the pattern and the string, we succeeded
			if(patIt == tmpPattern.end() && strIt == tmpStr.end())
				return true;
			else
				return false;
		}

		template <class T>
		static const typename BasicString<T> replaceAllInternal(const typename BasicString<T>& source,
			const typename BasicString<T>& replaceWhat, const typename BasicString<T>& replaceWithWhat)
		{
			BasicString<T> result = source;
			BasicString<T>::size_type pos = 0;
			while(1)
			{
				pos = result.find(replaceWhat, pos);
				if(pos == BasicString<T>::npos) break;
				result.replace(pos, replaceWhat.size(), replaceWithWhat);
				pos += replaceWithWhat.size();
			}
			return result;
		}

	};


	//---------------------------------------
	//	TYPE to STRING conversions
	//---------------------------------------

	// convert narrow string to wide string
	WString toWString(const String& source);

	// convert float to wide string
	WString toWString(float val, ushort precision = 6,
		ushort width = 0, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert double to wide string
	WString toWString(double val, ushort precision = 6,
		ushort width = 0, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert int to wide string
	WString toWString(int32 val, ushort width, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert unsigned int to a wide string
	WString toWString(uint32 val, ushort width, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert a 64 bit integer to wide string
	WString toWString(int64 val, ushort width, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert a 64 bit unsigned integer to a wide string
	WString toWString(uint64 val, ushort width, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert a narrow char unsigned to a wide string
	WString toWString(char val, ushort width, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert int to wide string
	WString toWString(wchar_t val, ushort width, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert a boolean to a string
	WString toWString(bool val, bool yesNo = false);



	// convert narrow string to wide string
	String toString(const WString& source);

	// convert float to wide string
	String toString(float val, ushort precision = 6,
		ushort width = 0, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert double to wide string
	String toString(double val, ushort precision = 6,
		ushort width = 0, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert int to wide string
	String toString(int32 val, ushort width, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert unsigned int to a wide string
	String toString(uint32 val, ushort width, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert a 64 bit integer to wide string
	String toString(int64 val, ushort width, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert a 64 bit unsigned integer to a wide string
	String toString(uint64 val, ushort width, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert a narrow char unsigned to a wide string
	String toString(char val, ushort width, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert int to wide string
	String toString(wchar_t val, ushort width, char fill = ' ',
		std::ios::fmtflags flags = std::ios::fmtflags(0));

	// convert a boolean to a string
	String toString(bool val, bool yesNo = false);

	// TODO: add Vector2, Vector3, Vector4, Matrix3, Matrix4, Quaternion, and other custom stuff here


	//---------------------------------------
	//	STRING to TYPE conversions
	//---------------------------------------

	// convertes a string to a float (0.0f) if the value could not be parsed
	float parseFloat(const String& val, float defaultValue = 0);

	// convertes a string to a float (0.0f) if the value could not be parsed
	int32 parseInt(const String& val, int32 defaultValue = 0);

	// convertes a string to a float (0.0f) if the value could not be parsed
	uint32 parseUnsignedInt(const String& val, uint32 defaultValue = 0);

	// convertes a string to a float (0.0f) if the value could not be parsed
	long parseLong(const String& val, long defaultValue = 0);

	// convertes a string to a float (0.0f) if the value could not be parsed
	unsigned long parseUnsignedLong(const String& val, unsigned long defaultValue = 0);

	// convertes a string to a bool, tries matching the start of the string to "true", "yes" or "1" false otherwise
	bool parseBool(const String& val, bool defaultValue = 0);

	// is the string a valid number value
	bool isNumber(const String& val);



	// convertes a string to a float (0.0f) if the value could not be parsed
	float parseFloat(const WString& val, float defaultValue = 0);

	// convertes a string to a float (0.0f) if the value could not be parsed
	int32 parseInt(const WString& val, int32 defaultValue = 0);

	// convertes a string to a float (0.0f) if the value could not be parsed
	uint32 parseUnsignedInt(const WString& val, uint32 defaultValue = 0);

	// convertes a string to a float (0.0f) if the value could not be parsed
	long parseLong(const WString& val, long defaultValue = 0);

	// convertes a string to a float (0.0f) if the value could not be parsed
	unsigned long parseUnsignedLong(const WString& val, unsigned long defaultValue = 0);

	// convertes a string to a bool, tries matching the start of the string to "true", "yes" or "1" false otherwise
	bool parseBool(const WString& val, bool defaultValue = 0);

	// is the string a valid number value
	bool isNumber(const WString& val);


}

// there is no hash generation for basic_string so we need to provide our own.
template<>
struct std::hash<WackiEngine::String>
{
	size_t operator()(const WackiEngine::String& string) const
	{
		size_t hash = 0;
		for(size_t i = 0; i < string.size(); i++)
			hash = 65599 * hash + string[i];
		return hash ^ (hash >> 16);
	}
};

template<>
struct std::hash<WackiEngine::WString>
{
	size_t operator()(const WackiEngine::WString& string) const
	{
		size_t hash = 0;
		for(size_t i = 0; i < string.size(); i++)
			hash = 65599 * hash + string[i];
		return hash ^ (hash >> 16);
	}
};

#endif